﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Xml;
using System.Xml.Serialization;

namespace Sendine.Web.Route.Cfg
{
    /// <summary>
    /// Url路由配置
    /// </summary>
    public class RouterConfiguration : IXmlSerializable, IDisposable
    {
        ~RouterConfiguration()
        {
            Dispose();
        }
        public const string cSendineWeb = "sendine.web";
        public const string cRouterConfiguration = "router-config";
        public const string cProperty = "property";
        public const string cCacheKey = "RouterConfiguration";

        #region Fileds
        private List<RouterDomain> list;
        private Dictionary<string, object> properties;
        private string configFile;
        #endregion

        public RouterConfiguration()
        {
            properties = new Dictionary<string, object>();
            properties.Add(RouterKeys.cEnableFactory, true);
            properties.Add(RouterKeys.cAutoSave, true);
            list = new List<RouterDomain>();
        }

        #region Properties
        /// <summary>
        /// 获取<see cref="RouterDomain"/>配置项
        /// </summary>
        /// <param name="domainName">主机名</param>
        /// <returns></returns>
        public RouterDomain this[string domainName]
        {
            get { return GetRouterDomain(domainName, "*"); }
        }

        /// <summary>
        /// 获取<see cref="RouterDomain"/>配置项
        /// </summary>
        /// <param name="domainName">主机名</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public RouterDomain this[string domainName, string port]
        {
            get { return GetRouterDomain(domainName, port); }
        }
        #region 获取属性
        /// <summary>
        /// 获取属性
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetProperty(string name)
        {
            return properties[name].ToString();
        }

        /// <summary>
        /// 获取属性（泛型）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public T GetProperty<T>(string name)
        {
            return (T)Convert.ChangeType(properties[name], typeof(T));
        }

        /// <summary>
        /// 获取属性
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool GetBooleanProperty(string name)
        {
            bool result;
            Boolean.TryParse(GetProperty(name), out result);
            return result;
        }
        /// <summary>
        /// 获取属性
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public int GetIntProperty(string name)
        {
            int result;
            int.TryParse(GetProperty(name), out result);
            return result;
        }

        /// <summary>
        /// 设置属性
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public RouterConfiguration SetProperty(string name, object value)
        {
            if (properties.ContainsKey(name))
            {
                properties[name] = value;
            }
            else
            {
                properties.Add(name, value);
            }
            return this;
        }
        #endregion

        public RouterDomain CurrentDomain
        {
            get
            {
                return GetRouterDomain(HttpContext.Current.Request.Url.DnsSafeHost,
                                 HttpContext.Current.Request.Url.Port.ToString());
            }
        }

        #endregion

        #region IXmlSerializable
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(XmlReader reader)
        {
            if (reader.ReadToFollowing(cSendineWeb) && reader.ReadToFollowing(cRouterConfiguration))
            {
                XmlDocument document = new XmlDocument();
                document.Load(reader.ReadSubtree());
                XmlElement elHmData = document.DocumentElement;
                foreach (XmlElement elResult in elHmData.ChildNodes)
                {
                    if (elResult.Name.ToLower() != RouterDomain.cRouterDomain)
                    {
                        if (elResult.Name.ToLower() != cProperty)
                        {
                            continue;
                        }
                        else
                        {
                            this.SetProperty(elResult.Attributes["name"].Value, elResult.InnerText);
                            continue;
                        }
                    }
                    RouterDomain domain = new RouterDomain(elResult.GetAttribute(RouterDomain.cRouterDomainName),
                                                           elResult.GetAttribute(RouterDomain.cRouterDomainBound),
                                                           elResult.GetAttribute(RouterDomain.cRouterDomainPort));
                    foreach (XmlElement el in elResult.ChildNodes)
                    {
                        if (el.Name.ToLower() == RouterRule.cRouterRule)
                        {
                            RouterRule rule = new RouterRule(el.GetAttribute(RouterRule.cRuleRegex),
                                                             el.GetAttribute(RouterRule.cRuleTarget),
                                                             el.GetAttribute(RouterRule.cRuleValidator),
                                                             el.GetAttribute(RouterRule.cAssembly),
                                                             domain.Name,
                                                             el.GetAttribute(RouterRule.cReplace),
                                                             el.GetAttribute(RouterRule.cTrim));
                            domain.Add(rule);
                        }
                    }
                    list.Add(domain);
                }
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement(cSendineWeb, "http://www.sendine.com/schemas/sendine.web/");
            writer.WriteStartElement(cRouterConfiguration);
            foreach (RouterDomain domain in list)
            {
                domain.WriteXml(writer);
            }
            foreach (KeyValuePair<string, object> property in properties)
            {
                writer.WriteStartElement(cProperty);
                writer.WriteAttributeString("name", property.Key);
                writer.WriteString(property.Value.ToString());
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
        /// <summary>
        /// 转为完整的xml文档
        /// </summary>
        public string ToXml()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = System.Text.Encoding.UTF8;
            settings.Indent = true;
            settings.OmitXmlDeclaration = true;
            //settings.NewLineOnAttributes = true;
            XmlWriter writer = XmlWriter.Create(sb, settings);
            WriteXml(writer);
            writer.Close();
            return sb.ToString();
        }
        #endregion

        #region Methods
        /// <summary>
        /// 获取配置
        /// </summary>
        /// <returns></returns>
        public static RouterConfiguration GetConfig()
        {
            if (HttpContext.Current.Cache[cCacheKey] != null)
            {
                return (RouterConfiguration)HttpContext.Current.Cache[cCacheKey];
            }
            return null;
        }

        #region Config
        public void Config(string filepath)
        {
            using (FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
            {
                Config(stream);
            }
            configFile = filepath;
        }
        public void Config(Stream stream)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            settings.IgnoreWhitespace = true;
            XmlReader reader = XmlReader.Create(stream, settings);
            Config(reader);
            reader.Close();
            HttpContext.Current.Cache.Insert(cCacheKey, this);
        }
        public void Config(XmlReader reader)
        {
            ReadXml(reader);
        }
        #endregion

        /// <summary>
        /// 根据域名获取<see cref="RouterDomain"/>配置对象
        /// </summary>
        /// <param name="hostname"></param>
        /// <returns></returns>
        public RouterDomain GetRouterDomain(string hostname)
        {
            return GetRouterDomain(hostname, "*");
        }

        /// <summary>
        /// 根据域名获取<see cref="RouterDomain"/>配置对象
        /// </summary>
        /// <param name="hostname">主机名</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public RouterDomain GetRouterDomain(string hostname, string port)
        {
            foreach (RouterDomain domain in list)
            {
                if (domain.IsMatch(hostname, port))
                    return domain;
            }
            return new RouterDomain();
        }
        #endregion

        public void Dispose()
        {
            if (this.GetBooleanProperty(RouterKeys.cAutoSave))
            {
                if (!string.IsNullOrEmpty(configFile))
                    System.IO.File.WriteAllText(configFile, this.ToXml());
            }
            GC.SuppressFinalize(this);
        }
    }
}